home *** CD-ROM | disk | FTP | other *** search
/ Chip 2007 January, February, March & April / Chip-Cover-CD-2007-02.iso / Pakiet bezpieczenstwa / mini Pentoo LiveCD 2006.1 / mpentoo-2006.1.iso / livecd.squashfs / usr / lib / python2.4 / smtplib.pyo (.txt) < prev    next >
Python Compiled Bytecode  |  2005-10-18  |  25KB  |  823 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyo (Python 2.4)
  3.  
  4. '''SMTP/ESMTP client class.
  5.  
  6. This should follow RFC 821 (SMTP), RFC 1869 (ESMTP), RFC 2554 (SMTP
  7. Authentication) and RFC 2487 (Secure SMTP over TLS).
  8.  
  9. Notes:
  10.  
  11. Please remember, when doing ESMTP, that the names of the SMTP service
  12. extensions are NOT the same thing as the option keywords for the RCPT
  13. and MAIL commands!
  14.  
  15. Example:
  16.  
  17.   >>> import smtplib
  18.   >>> s=smtplib.SMTP("localhost")
  19.   >>> print s.help()
  20.   This is Sendmail version 8.8.4
  21.   Topics:
  22.       HELO    EHLO    MAIL    RCPT    DATA
  23.       RSET    NOOP    QUIT    HELP    VRFY
  24.       EXPN    VERB    ETRN    DSN
  25.   For more info use "HELP <topic>".
  26.   To report bugs in the implementation send email to
  27.       sendmail-bugs@sendmail.org.
  28.   For local information send email to Postmaster at your site.
  29.   End of HELP info
  30.   >>> s.putcmd("vrfy","someone@here")
  31.   >>> s.getreply()
  32.   (250, "Somebody OverHere <somebody@here.my.org>")
  33.   >>> s.quit()
  34. '''
  35. import socket
  36. import re
  37. import rfc822
  38. import base64
  39. import hmac
  40. from email.base64MIME import encode as encode_base64
  41. from sys import stderr
  42. __all__ = [
  43.     'SMTPException',
  44.     'SMTPServerDisconnected',
  45.     'SMTPResponseException',
  46.     'SMTPSenderRefused',
  47.     'SMTPRecipientsRefused',
  48.     'SMTPDataError',
  49.     'SMTPConnectError',
  50.     'SMTPHeloError',
  51.     'SMTPAuthenticationError',
  52.     'quoteaddr',
  53.     'quotedata',
  54.     'SMTP']
  55. SMTP_PORT = 25
  56. CRLF = '\r\n'
  57. OLDSTYLE_AUTH = re.compile('auth=(.*)', re.I)
  58.  
  59. class SMTPException(Exception):
  60.     '''Base class for all exceptions raised by this module.'''
  61.     pass
  62.  
  63.  
  64. class SMTPServerDisconnected(SMTPException):
  65.     '''Not connected to any SMTP server.
  66.  
  67.     This exception is raised when the server unexpectedly disconnects,
  68.     or when an attempt is made to use the SMTP instance before
  69.     connecting it to a server.
  70.     '''
  71.     pass
  72.  
  73.  
  74. class SMTPResponseException(SMTPException):
  75.     """Base class for all exceptions that include an SMTP error code.
  76.  
  77.     These exceptions are generated in some instances when the SMTP
  78.     server returns an error code.  The error code is stored in the
  79.     `smtp_code' attribute of the error, and the `smtp_error' attribute
  80.     is set to the error message.
  81.     """
  82.     
  83.     def __init__(self, code, msg):
  84.         self.smtp_code = code
  85.         self.smtp_error = msg
  86.         self.args = (code, msg)
  87.  
  88.  
  89.  
  90. class SMTPSenderRefused(SMTPResponseException):
  91.     """Sender address refused.
  92.  
  93.     In addition to the attributes set by on all SMTPResponseException
  94.     exceptions, this sets `sender' to the string that the SMTP refused.
  95.     """
  96.     
  97.     def __init__(self, code, msg, sender):
  98.         self.smtp_code = code
  99.         self.smtp_error = msg
  100.         self.sender = sender
  101.         self.args = (code, msg, sender)
  102.  
  103.  
  104.  
  105. class SMTPRecipientsRefused(SMTPException):
  106.     """All recipient addresses refused.
  107.  
  108.     The errors for each recipient are accessible through the attribute
  109.     'recipients', which is a dictionary of exactly the same sort as
  110.     SMTP.sendmail() returns.
  111.     """
  112.     
  113.     def __init__(self, recipients):
  114.         self.recipients = recipients
  115.         self.args = (recipients,)
  116.  
  117.  
  118.  
  119. class SMTPDataError(SMTPResponseException):
  120.     """The SMTP server didn't accept the data."""
  121.     pass
  122.  
  123.  
  124. class SMTPConnectError(SMTPResponseException):
  125.     '''Error during connection establishment.'''
  126.     pass
  127.  
  128.  
  129. class SMTPHeloError(SMTPResponseException):
  130.     '''The server refused our HELO reply.'''
  131.     pass
  132.  
  133.  
  134. class SMTPAuthenticationError(SMTPResponseException):
  135.     """Authentication error.
  136.  
  137.     Most probably the server didn't accept the username/password
  138.     combination provided.
  139.     """
  140.     pass
  141.  
  142.  
  143. class SSLFakeSocket:
  144.     '''A fake socket object that really wraps a SSLObject.
  145.  
  146.     It only supports what is needed in smtplib.
  147.     '''
  148.     
  149.     def __init__(self, realsock, sslobj):
  150.         self.realsock = realsock
  151.         self.sslobj = sslobj
  152.  
  153.     
  154.     def send(self, str):
  155.         self.sslobj.write(str)
  156.         return len(str)
  157.  
  158.     sendall = send
  159.     
  160.     def close(self):
  161.         self.realsock.close()
  162.  
  163.  
  164.  
  165. class SSLFakeFile:
  166.     '''A fake file like object that really wraps a SSLObject.
  167.  
  168.     It only supports what is needed in smtplib.
  169.     '''
  170.     
  171.     def __init__(self, sslobj):
  172.         self.sslobj = sslobj
  173.  
  174.     
  175.     def readline(self):
  176.         str = ''
  177.         chr = None
  178.         while chr != '\n':
  179.             chr = self.sslobj.read(1)
  180.             str += chr
  181.         return str
  182.  
  183.     
  184.     def close(self):
  185.         pass
  186.  
  187.  
  188.  
  189. def quoteaddr(addr):
  190.     '''Quote a subset of the email addresses defined by RFC 821.
  191.  
  192.     Should be able to handle anything rfc822.parseaddr can handle.
  193.     '''
  194.     m = (None, None)
  195.     
  196.     try:
  197.         m = rfc822.parseaddr(addr)[1]
  198.     except AttributeError:
  199.         pass
  200.  
  201.     if m == (None, None):
  202.         return '<%s>' % addr
  203.     else:
  204.         return '<%s>' % m
  205.  
  206.  
  207. def quotedata(data):
  208.     """Quote data for email.
  209.  
  210.     Double leading '.', and change Unix newline '\\n', or Mac '\\r' into
  211.     Internet CRLF end-of-line.
  212.     """
  213.     return re.sub('(?m)^\\.', '..', re.sub('(?:\\r\\n|\\n|\\r(?!\\n))', CRLF, data))
  214.  
  215.  
  216. class SMTP:
  217.     """This class manages a connection to an SMTP or ESMTP server.
  218.     SMTP Objects:
  219.         SMTP objects have the following attributes:
  220.             helo_resp
  221.                 This is the message given by the server in response to the
  222.                 most recent HELO command.
  223.  
  224.             ehlo_resp
  225.                 This is the message given by the server in response to the
  226.                 most recent EHLO command. This is usually multiline.
  227.  
  228.             does_esmtp
  229.                 This is a True value _after you do an EHLO command_, if the
  230.                 server supports ESMTP.
  231.  
  232.             esmtp_features
  233.                 This is a dictionary, which, if the server supports ESMTP,
  234.                 will _after you do an EHLO command_, contain the names of the
  235.                 SMTP service extensions this server supports, and their
  236.                 parameters (if any).
  237.  
  238.                 Note, all extension names are mapped to lower case in the
  239.                 dictionary.
  240.  
  241.         See each method's docstrings for details.  In general, there is a
  242.         method of the same name to perform each SMTP command.  There is also a
  243.         method called 'sendmail' that will do an entire mail transaction.
  244.         """
  245.     debuglevel = 0
  246.     file = None
  247.     helo_resp = None
  248.     ehlo_resp = None
  249.     does_esmtp = 0
  250.     
  251.     def __init__(self, host = '', port = 0, local_hostname = None):
  252.         """Initialize a new instance.
  253.  
  254.         If specified, `host' is the name of the remote host to which to
  255.         connect.  If specified, `port' specifies the port to which to connect.
  256.         By default, smtplib.SMTP_PORT is used.  An SMTPConnectError is raised
  257.         if the specified `host' doesn't respond correctly.  If specified,
  258.         `local_hostname` is used as the FQDN of the local host.  By default,
  259.         the local hostname is found using socket.getfqdn().
  260.  
  261.         """
  262.         self.esmtp_features = { }
  263.         if host:
  264.             (code, msg) = self.connect(host, port)
  265.             if code != 220:
  266.                 raise SMTPConnectError(code, msg)
  267.             
  268.         
  269.         if local_hostname is not None:
  270.             self.local_hostname = local_hostname
  271.         else:
  272.             fqdn = socket.getfqdn()
  273.             if '.' in fqdn:
  274.                 self.local_hostname = fqdn
  275.             else:
  276.                 addr = socket.gethostbyname(socket.gethostname())
  277.                 self.local_hostname = '[%s]' % addr
  278.  
  279.     
  280.     def set_debuglevel(self, debuglevel):
  281.         '''Set the debug output level.
  282.  
  283.         A non-false value results in debug messages for connection and for all
  284.         messages sent to and received from the server.
  285.  
  286.         '''
  287.         self.debuglevel = debuglevel
  288.  
  289.     
  290.     def connect(self, host = 'localhost', port = 0):
  291.         """Connect to a host on a given port.
  292.  
  293.         If the hostname ends with a colon (`:') followed by a number, and
  294.         there is no port specified, that suffix will be stripped off and the
  295.         number interpreted as the port number to use.
  296.  
  297.         Note: This method is automatically invoked by __init__, if a host is
  298.         specified during instantiation.
  299.  
  300.         """
  301.         if not port and host.find(':') == host.rfind(':'):
  302.             i = host.rfind(':')
  303.             if i >= 0:
  304.                 host = host[:i]
  305.                 port = host[i + 1:]
  306.                 
  307.                 try:
  308.                     port = int(port)
  309.                 except ValueError:
  310.                     raise socket.error, 'nonnumeric port'
  311.                 except:
  312.                     None<EXCEPTION MATCH>ValueError
  313.                 
  314.  
  315.             None<EXCEPTION MATCH>ValueError
  316.         
  317.         if not port:
  318.             port = SMTP_PORT
  319.         
  320.         if self.debuglevel > 0:
  321.             print >>stderr, 'connect:', (host, port)
  322.         
  323.         msg = 'getaddrinfo returns an empty list'
  324.         self.sock = None
  325.         for res in socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM):
  326.             (af, socktype, proto, canonname, sa) = res
  327.             
  328.             try:
  329.                 self.sock = socket.socket(af, socktype, proto)
  330.                 if self.debuglevel > 0:
  331.                     print >>stderr, 'connect:', (host, port)
  332.                 
  333.                 self.sock.connect(sa)
  334.             except socket.error:
  335.                 msg = None
  336.                 if self.debuglevel > 0:
  337.                     print >>stderr, 'connect fail:', (host, port)
  338.                 
  339.                 if self.sock:
  340.                     self.sock.close()
  341.                 
  342.                 self.sock = None
  343.                 continue
  344.  
  345.             break
  346.         
  347.         if not self.sock:
  348.             raise socket.error, msg
  349.         
  350.         (code, msg) = self.getreply()
  351.         if self.debuglevel > 0:
  352.             print >>stderr, 'connect:', msg
  353.         
  354.         return (code, msg)
  355.  
  356.     
  357.     def send(self, str):
  358.         """Send `str' to the server."""
  359.         if self.debuglevel > 0:
  360.             print >>stderr, 'send:', repr(str)
  361.         
  362.         if self.sock:
  363.             
  364.             try:
  365.                 self.sock.sendall(str)
  366.             except socket.error:
  367.                 self.close()
  368.                 raise SMTPServerDisconnected('Server not connected')
  369.             except:
  370.                 None<EXCEPTION MATCH>socket.error
  371.             
  372.  
  373.         None<EXCEPTION MATCH>socket.error
  374.         raise SMTPServerDisconnected('please run connect() first')
  375.  
  376.     
  377.     def putcmd(self, cmd, args = ''):
  378.         '''Send a command to the server.'''
  379.         if args == '':
  380.             str = '%s%s' % (cmd, CRLF)
  381.         else:
  382.             str = '%s %s%s' % (cmd, args, CRLF)
  383.         self.send(str)
  384.  
  385.     
  386.     def getreply(self):
  387.         """Get a reply from the server.
  388.  
  389.         Returns a tuple consisting of:
  390.  
  391.           - server response code (e.g. '250', or such, if all goes well)
  392.             Note: returns -1 if it can't read response code.
  393.  
  394.           - server response string corresponding to response code (multiline
  395.             responses are converted to a single, multiline string).
  396.  
  397.         Raises SMTPServerDisconnected if end-of-file is reached.
  398.         """
  399.         resp = []
  400.         if self.file is None:
  401.             self.file = self.sock.makefile('rb')
  402.         
  403.         while None:
  404.             line = self.file.readline()
  405.             if line == '':
  406.                 self.close()
  407.                 raise SMTPServerDisconnected('Connection unexpectedly closed')
  408.             
  409.             if self.debuglevel > 0:
  410.                 print >>stderr, 'reply:', repr(line)
  411.             
  412.             code = line[:3]
  413.             
  414.             try:
  415.                 errcode = int(code)
  416.             except ValueError:
  417.                 errcode = -1
  418.                 break
  419.  
  420.             if line[3:4] != '-':
  421.                 break
  422.                 continue
  423.         errmsg = '\n'.join(resp)
  424.         if self.debuglevel > 0:
  425.             print >>stderr, 'reply: retcode (%s); Msg: %s' % (errcode, errmsg)
  426.         
  427.         return (errcode, errmsg)
  428.  
  429.     
  430.     def docmd(self, cmd, args = ''):
  431.         '''Send a command, and return its response code.'''
  432.         self.putcmd(cmd, args)
  433.         return self.getreply()
  434.  
  435.     
  436.     def helo(self, name = ''):
  437.         """SMTP 'helo' command.
  438.         Hostname to send for this command defaults to the FQDN of the local
  439.         host.
  440.         """
  441.         if not name:
  442.             pass
  443.         self.putcmd('helo', self.local_hostname)
  444.         (code, msg) = self.getreply()
  445.         self.helo_resp = msg
  446.         return (code, msg)
  447.  
  448.     
  449.     def ehlo(self, name = ''):
  450.         """ SMTP 'ehlo' command.
  451.         Hostname to send for this command defaults to the FQDN of the local
  452.         host.
  453.         """
  454.         self.esmtp_features = { }
  455.         if not name:
  456.             pass
  457.         self.putcmd('ehlo', self.local_hostname)
  458.         (code, msg) = self.getreply()
  459.         if code == -1 and len(msg) == 0:
  460.             self.close()
  461.             raise SMTPServerDisconnected('Server not connected')
  462.         
  463.         self.ehlo_resp = msg
  464.         if code != 250:
  465.             return (code, msg)
  466.         
  467.         self.does_esmtp = 1
  468.         resp = self.ehlo_resp.split('\n')
  469.         del resp[0]
  470.         for each in resp:
  471.             auth_match = OLDSTYLE_AUTH.match(each)
  472.             if auth_match:
  473.                 self.esmtp_features['auth'] = self.esmtp_features.get('auth', '') + ' ' + auth_match.groups(0)[0]
  474.                 continue
  475.             
  476.             m = re.match('(?P<feature>[A-Za-z0-9][A-Za-z0-9\\-]*) ?', each)
  477.             if m:
  478.                 feature = m.group('feature').lower()
  479.                 params = m.string[m.end('feature'):].strip()
  480.                 if feature == 'auth':
  481.                     self.esmtp_features[feature] = self.esmtp_features.get(feature, '') + ' ' + params
  482.                 else:
  483.                     self.esmtp_features[feature] = params
  484.             feature == 'auth'
  485.         
  486.         return (code, msg)
  487.  
  488.     
  489.     def has_extn(self, opt):
  490.         '''Does the server support a given SMTP service extension?'''
  491.         return opt.lower() in self.esmtp_features
  492.  
  493.     
  494.     def help(self, args = ''):
  495.         """SMTP 'help' command.
  496.         Returns help text from server."""
  497.         self.putcmd('help', args)
  498.         return self.getreply()
  499.  
  500.     
  501.     def rset(self):
  502.         """SMTP 'rset' command -- resets session."""
  503.         return self.docmd('rset')
  504.  
  505.     
  506.     def noop(self):
  507.         """SMTP 'noop' command -- doesn't do anything :>"""
  508.         return self.docmd('noop')
  509.  
  510.     
  511.     def mail(self, sender, options = []):
  512.         """SMTP 'mail' command -- begins mail xfer session."""
  513.         optionlist = ''
  514.         if options and self.does_esmtp:
  515.             optionlist = ' ' + ' '.join(options)
  516.         
  517.         self.putcmd('mail', 'FROM:%s%s' % (quoteaddr(sender), optionlist))
  518.         return self.getreply()
  519.  
  520.     
  521.     def rcpt(self, recip, options = []):
  522.         """SMTP 'rcpt' command -- indicates 1 recipient for this mail."""
  523.         optionlist = ''
  524.         if options and self.does_esmtp:
  525.             optionlist = ' ' + ' '.join(options)
  526.         
  527.         self.putcmd('rcpt', 'TO:%s%s' % (quoteaddr(recip), optionlist))
  528.         return self.getreply()
  529.  
  530.     
  531.     def data(self, msg):
  532.         """SMTP 'DATA' command -- sends message data to server.
  533.  
  534.         Automatically quotes lines beginning with a period per rfc821.
  535.         Raises SMTPDataError if there is an unexpected reply to the
  536.         DATA command; the return value from this method is the final
  537.         response code received when the all data is sent.
  538.         """
  539.         self.putcmd('data')
  540.         (code, repl) = self.getreply()
  541.         if self.debuglevel > 0:
  542.             print >>stderr, 'data:', (code, repl)
  543.         
  544.         if code != 354:
  545.             raise SMTPDataError(code, repl)
  546.         else:
  547.             q = quotedata(msg)
  548.             if q[-2:] != CRLF:
  549.                 q = q + CRLF
  550.             
  551.             q = q + '.' + CRLF
  552.             self.send(q)
  553.             (code, msg) = self.getreply()
  554.             if self.debuglevel > 0:
  555.                 print >>stderr, 'data:', (code, msg)
  556.             
  557.             return (code, msg)
  558.  
  559.     
  560.     def verify(self, address):
  561.         """SMTP 'verify' command -- checks for address validity."""
  562.         self.putcmd('vrfy', quoteaddr(address))
  563.         return self.getreply()
  564.  
  565.     vrfy = verify
  566.     
  567.     def expn(self, address):
  568.         """SMTP 'verify' command -- checks for address validity."""
  569.         self.putcmd('expn', quoteaddr(address))
  570.         return self.getreply()
  571.  
  572.     
  573.     def login(self, user, password):
  574.         """Log in on an SMTP server that requires authentication.
  575.  
  576.         The arguments are:
  577.             - user:     The user name to authenticate with.
  578.             - password: The password for the authentication.
  579.  
  580.         If there has been no previous EHLO or HELO command this session, this
  581.         method tries ESMTP EHLO first.
  582.  
  583.         This method will return normally if the authentication was successful.
  584.  
  585.         This method may raise the following exceptions:
  586.  
  587.          SMTPHeloError            The server didn't reply properly to
  588.                                   the helo greeting.
  589.          SMTPAuthenticationError  The server didn't accept the username/
  590.                                   password combination.
  591.          SMTPException            No suitable authentication method was
  592.                                   found.
  593.         """
  594.         
  595.         def encode_cram_md5(challenge, user, password):
  596.             challenge = base64.decodestring(challenge)
  597.             response = user + ' ' + hmac.HMAC(password, challenge).hexdigest()
  598.             return encode_base64(response, eol = '')
  599.  
  600.         
  601.         def encode_plain(user, password):
  602.             return encode_base64('%s\x00%s\x00%s' % (user, user, password), eol = '')
  603.  
  604.         AUTH_PLAIN = 'PLAIN'
  605.         AUTH_CRAM_MD5 = 'CRAM-MD5'
  606.         AUTH_LOGIN = 'LOGIN'
  607.         if self.helo_resp is None and self.ehlo_resp is None:
  608.             if self.ehlo()[0] <= self.ehlo()[0]:
  609.                 pass
  610.             elif not self.ehlo()[0] <= 299:
  611.                 (code, resp) = self.helo()
  612.                 if code <= code:
  613.                     pass
  614.                 elif not code <= 299:
  615.                     raise SMTPHeloError(code, resp)
  616.                 
  617.             
  618.         
  619.         if not self.has_extn('auth'):
  620.             raise SMTPException('SMTP AUTH extension not supported by server.')
  621.         
  622.         authlist = self.esmtp_features['auth'].split()
  623.         preferred_auths = [
  624.             AUTH_CRAM_MD5,
  625.             AUTH_PLAIN,
  626.             AUTH_LOGIN]
  627.         authmethod = None
  628.         for method in preferred_auths:
  629.             if method in authlist:
  630.                 authmethod = method
  631.                 break
  632.                 continue
  633.         
  634.         if authmethod == AUTH_CRAM_MD5:
  635.             (code, resp) = self.docmd('AUTH', AUTH_CRAM_MD5)
  636.             if code == 503:
  637.                 return (code, resp)
  638.             
  639.             (code, resp) = self.docmd(encode_cram_md5(resp, user, password))
  640.         elif authmethod == AUTH_PLAIN:
  641.             (code, resp) = self.docmd('AUTH', AUTH_PLAIN + ' ' + encode_plain(user, password))
  642.         elif authmethod == AUTH_LOGIN:
  643.             (code, resp) = self.docmd('AUTH', '%s %s' % (AUTH_LOGIN, encode_base64(user, eol = '')))
  644.             if code != 334:
  645.                 raise SMTPAuthenticationError(code, resp)
  646.             
  647.             (code, resp) = self.docmd(encode_base64(password, eol = ''))
  648.         elif authmethod is None:
  649.             raise SMTPException('No suitable authentication method found.')
  650.         
  651.         if code not in [
  652.             235,
  653.             503]:
  654.             raise SMTPAuthenticationError(code, resp)
  655.         
  656.         return (code, resp)
  657.  
  658.     
  659.     def starttls(self, keyfile = None, certfile = None):
  660.         '''Puts the connection to the SMTP server into TLS mode.
  661.  
  662.         If the server supports TLS, this will encrypt the rest of the SMTP
  663.         session. If you provide the keyfile and certfile parameters,
  664.         the identity of the SMTP server and client can be checked. This,
  665.         however, depends on whether the socket module really checks the
  666.         certificates.
  667.         '''
  668.         (resp, reply) = self.docmd('STARTTLS')
  669.         if resp == 220:
  670.             sslobj = socket.ssl(self.sock, keyfile, certfile)
  671.             self.sock = SSLFakeSocket(self.sock, sslobj)
  672.             self.file = SSLFakeFile(sslobj)
  673.         
  674.         return (resp, reply)
  675.  
  676.     
  677.     def sendmail(self, from_addr, to_addrs, msg, mail_options = [], rcpt_options = []):
  678.         '''This command performs an entire mail transaction.
  679.  
  680.         The arguments are:
  681.             - from_addr    : The address sending this mail.
  682.             - to_addrs     : A list of addresses to send this mail to.  A bare
  683.                              string will be treated as a list with 1 address.
  684.             - msg          : The message to send.
  685.             - mail_options : List of ESMTP options (such as 8bitmime) for the
  686.                              mail command.
  687.             - rcpt_options : List of ESMTP options (such as DSN commands) for
  688.                              all the rcpt commands.
  689.  
  690.         If there has been no previous EHLO or HELO command this session, this
  691.         method tries ESMTP EHLO first.  If the server does ESMTP, message size
  692.         and each of the specified options will be passed to it.  If EHLO
  693.         fails, HELO will be tried and ESMTP options suppressed.
  694.  
  695.         This method will return normally if the mail is accepted for at least
  696.         one recipient.  It returns a dictionary, with one entry for each
  697.         recipient that was refused.  Each entry contains a tuple of the SMTP
  698.         error code and the accompanying error message sent by the server.
  699.  
  700.         This method may raise the following exceptions:
  701.  
  702.          SMTPHeloError          The server didn\'t reply properly to
  703.                                 the helo greeting.
  704.          SMTPRecipientsRefused  The server rejected ALL recipients
  705.                                 (no mail was sent).
  706.          SMTPSenderRefused      The server didn\'t accept the from_addr.
  707.          SMTPDataError          The server replied with an unexpected
  708.                                 error code (other than a refusal of
  709.                                 a recipient).
  710.  
  711.         Note: the connection will be open even after an exception is raised.
  712.  
  713.         Example:
  714.  
  715.          >>> import smtplib
  716.          >>> s=smtplib.SMTP("localhost")
  717.          >>> tolist=["one@one.org","two@two.org","three@three.org","four@four.org"]
  718.          >>> msg = \'\'\'\\
  719.          ... From: Me@my.org
  720.          ... Subject: testin\'...
  721.          ...
  722.          ... This is a test \'\'\'
  723.          >>> s.sendmail("me@my.org",tolist,msg)
  724.          { "three@three.org" : ( 550 ,"User unknown" ) }
  725.          >>> s.quit()
  726.  
  727.         In the above example, the message was accepted for delivery to three
  728.         of the four addresses, and one was rejected, with the error code
  729.         550.  If all addresses are accepted, then the method will return an
  730.         empty dictionary.
  731.  
  732.         '''
  733.         if self.helo_resp is None and self.ehlo_resp is None:
  734.             if self.ehlo()[0] <= self.ehlo()[0]:
  735.                 pass
  736.             elif not self.ehlo()[0] <= 299:
  737.                 (code, resp) = self.helo()
  738.                 if code <= code:
  739.                     pass
  740.                 elif not code <= 299:
  741.                     raise SMTPHeloError(code, resp)
  742.                 
  743.             
  744.         
  745.         esmtp_opts = []
  746.         if self.does_esmtp:
  747.             if self.has_extn('size'):
  748.                 esmtp_opts.append('size=%d' % len(msg))
  749.             
  750.             for option in mail_options:
  751.                 esmtp_opts.append(option)
  752.             
  753.         
  754.         (code, resp) = self.mail(from_addr, esmtp_opts)
  755.         if code != 250:
  756.             self.rset()
  757.             raise SMTPSenderRefused(code, resp, from_addr)
  758.         
  759.         senderrs = { }
  760.         if isinstance(to_addrs, basestring):
  761.             to_addrs = [
  762.                 to_addrs]
  763.         
  764.         for each in to_addrs:
  765.             (code, resp) = self.rcpt(each, rcpt_options)
  766.             if code != 250 and code != 251:
  767.                 senderrs[each] = (code, resp)
  768.                 continue
  769.         
  770.         if len(senderrs) == len(to_addrs):
  771.             self.rset()
  772.             raise SMTPRecipientsRefused(senderrs)
  773.         
  774.         (code, resp) = self.data(msg)
  775.         if code != 250:
  776.             self.rset()
  777.             raise SMTPDataError(code, resp)
  778.         
  779.         return senderrs
  780.  
  781.     
  782.     def close(self):
  783.         '''Close the connection to the SMTP server.'''
  784.         if self.file:
  785.             self.file.close()
  786.         
  787.         self.file = None
  788.         if self.sock:
  789.             self.sock.close()
  790.         
  791.         self.sock = None
  792.  
  793.     
  794.     def quit(self):
  795.         '''Terminate the SMTP session.'''
  796.         self.docmd('quit')
  797.         self.close()
  798.  
  799.  
  800. if __name__ == '__main__':
  801.     import sys
  802.     
  803.     def prompt(prompt):
  804.         sys.stdout.write(prompt + ': ')
  805.         return sys.stdin.readline().strip()
  806.  
  807.     fromaddr = prompt('From')
  808.     toaddrs = prompt('To').split(',')
  809.     print 'Enter message, end with ^D:'
  810.     msg = ''
  811.     while 1:
  812.         line = sys.stdin.readline()
  813.         if not line:
  814.             break
  815.         
  816.         msg = msg + line
  817.     print 'Message length is %d' % len(msg)
  818.     server = SMTP('localhost')
  819.     server.set_debuglevel(1)
  820.     server.sendmail(fromaddr, toaddrs, msg)
  821.     server.quit()
  822.  
  823.